﻿

using System;
using System.Collections;
using System.Linq.Expressions;
using System.Xml.Linq;

namespace 贪吃蛇
{
    internal class Play : Game
    {
        public static E_Speed speed = E_Speed.Slow;
        bool gameOver = false;
        bool press = false;
        public static Change change = new Change();
        public static Box box = new Box();
        List<Pos> grids = new List<Pos>();
        Pos[] buttonGrids = new Pos[bufWidth - 1];
        Pos[] rightSideGrids = new Pos[bufHeight - 1];
        Pos pos = new Pos(bufWidth / 2 - 1, 0);
        public override void play()
        {
            //reset();
            Console.Clear();
            writeAndAddBlock();
            Thread thread = new Thread(waitInput);
            thread.Start();
            while (!gameOver)
            {
                boxSpawn(); //生成方块形状
                pos.x = bufWidth / 2 - 1;
                pos.y = 0;
                box.move(pos);
                if(isCrush(box))
                {
                    gameOver = true;
                    box.write();
                    break;
                }
                
                //box.shape = E_Shape.N121;//test
                while (true) //一轮下落循环
                {
                    box.erase();
                    if (change.isChange)
                    {   
                        if(change.speed)
                            pos.y++;
                        if(change.direct != 0)
                        {
                            if (change.direct == -1)
                                pos.x = pos.x - 2 < 2 ? pos.x : pos.x - 2;
                            else
                                pos.x = pos.x + box.width * 2 >= bufWidth - 2 ? pos.x : pos.x + 2;

                        }
                        if (change.shape != -1)
                            tryChange((E_TransShape)change.shape);
                        change.reset();
                    }
                    box.move(pos);//下降的点
                    if (isCrush(box) ) //如果到底了或者接触方格了
                    {
                        //位置加入grid
                        foreach (Pos pos in box.poses) //需要创建个pos记录crush前的位置
                        {
                            grids.Add(new Pos(pos.x, pos.y - 1));
                        }
                        if (fullLine().Count > 0) //需不需要clearline,返回满了的行
                        {
                            clearGrid();//擦除所有格子
                            List<int> list = fullLine();
                            list.Sort();
                            for(int j = 0;j <list.Count; j++)
                            {
                                for (int i = 0; i < grids.Count; i++)
                                {
                                    if (grids[i].y == list[j])
                                    {
                                        grids.RemoveAt(i);
                                    }
                                }
                                for (int i = 0; i < grids.Count; i++)
                                {
                                    if(grids[i].y < list[j])
                                    {
                                        Pos pos = grids[i];
                                        pos.y++;
                                        grids[i] = pos;
                                    }
                                }
                                
                            } //修改、移除格子
                        }
                        writeGrid(); //重绘所有格子
                        //}
                        change.fall = false;
                        break;
                    }
                    box.write(); //根据形状、方向写方块的形状
                    if(!change.fall)
                        Thread.Sleep((int)speed); //显示
                    pos.y++;
                }
            }
            //游戏结束
            writeOverInfo();
            Game.scene = E_Scene.Over;
            #region 等待用户输入确认
            while(Console.KeyAvailable){ 
                Console.ReadKey(true);
            }
            Console.ReadKey(true);
            reset();
            #endregion
        }
        private void clearGrid()
        {
            foreach (Pos pos in grids)
            {
                Console.SetCursorPosition(pos.x, pos.y);
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write("  ");
            }
        }

        private void writeGrid()
        {
            foreach(Pos pos in  grids)
            {
                Console.SetCursorPosition(pos.x, pos.y);
                Console.ForegroundColor = ConsoleColor.DarkGray;
                Console.Write(box.grid);
            }

        }
        private List<int> fullLine()
        {
            List<int> fullLine = new List<int>();
            int maxh = pos.y - 1 + 3 > bufHeight - 2 ? bufHeight - 2 : pos.y - 1 + 3;
            while (maxh >= pos.y - 1)
            {
                for (int i = 2; i < bufWidth - 2; i+=2)
                {
                    Pos pos = new Pos(i, maxh);
                    if (!grids.Contains(pos))
                        break;
                    if(i == bufWidth - 3 || i == bufWidth - 4)
                    {
                        fullLine.Add(maxh);
                    }
                }
                maxh--;
            }
            return fullLine;

        }
        internal bool isCrush(Box curBox)
        {
            if(curBox.poses ==null) return false;
            foreach (Pos pos in buttonGrids)
            {
                foreach (Pos boxPos in curBox.poses)
                {
                    if (pos.x == boxPos.x && pos.y == boxPos.y)
                    {
                        return true;
                    }
                }
            }
            if(grids == null)
                return false;
            foreach (Pos pos in grids)
            {
                foreach (Pos boxPos in curBox.poses)
                {
                    if (pos.x == boxPos.x && pos.y == boxPos.y)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        private void tryChange(E_TransShape shape)
        {
            Box newBox = new Box();
            newBox.shape = box.shape;
            newBox.poses = box.poses;
            newBox.transShape = shape;
            newBox.move(pos);
            if (isCrush(newBox) || pos.x + newBox.width * 2 >= bufWidth - 2)
            {
                return;
            }
            box.transShape = shape;
            
        }
        private void boxSpawn()
        {
            Random r = new Random();
            box = new Box((E_Shape)r.Next(0, 4));
        }

        private void reset()
        {

            gameOver = false;
            press = false;
        }



        private void writeOverInfo()
        {
            Console.SetCursorPosition(bufWidth / 2 - 2, bufHeight / 2);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("游戏结束!");
        }



        public void waitInput()
        {
            int minIndex = 0, maxIndex = 3;
            int index = minIndex;
            while (!gameOver)
            {
                switch (Console.ReadKey(true).Key)
                {
                    case ConsoleKey.S:
                        change.isChange = true;
                        change.speed = true;
                        break;
                    case ConsoleKey.A:
                        index = index + 1 > maxIndex ? minIndex : index + 1;
                        change.isChange = true;
                        change.shape = index;
                        break;
                    case ConsoleKey.D:
                        index = index - 1 < minIndex ? maxIndex : index - 1;
                        change.isChange = true;
                        change.shape = index;
                        break;
                    case ConsoleKey.LeftArrow:
                        change.isChange = true;
                        change.direct = -1;
                        break;
                    case ConsoleKey.RightArrow:
                        change.isChange = true;
                        change.direct = 1;
                        break;
                    case ConsoleKey.Enter:
                    case ConsoleKey.Spacebar:
                        //直接下落
                        change.isChange = true;
                        change.fall = true;
                        break;
                    default:
                        press = true;
                        break;
                }
            }

        }



        private void writeAndAddBlock()
        {
            String block = "■";
            Console.ForegroundColor = ConsoleColor.Red;
            //bottom
            for (int i = 0; i < bufWidth - 1; i++)
            {
                Console.SetCursorPosition(i, bufHeight - 1);
                buttonGrids[i] = new Pos (i, bufHeight - 1);
                Console.Write(block);
            }
            //left and right
            for (int j = 0; j < bufHeight - 1; j++)
            {
                Console.SetCursorPosition(0, j);
                Console.Write(block);
                Console.SetCursorPosition(bufWidth - 2, j);
                rightSideGrids[j] = new Pos(j, bufHeight - 1);
                Console.Write(block);
            }
        }
     
    }
    class Change
    {
        public bool isChange;
        public int shape = -1;
        public int direct = 0; // -1 , 0 , 1
        public bool speed;
        public bool fall;
        public Change()
        {
            
        }
        public void reset()
        {
            isChange = false;
            shape = -1;
            direct = 0;
            speed = false;
        }
    }
    class Box
    {
        internal string grid = "■";
        internal E_Shape shape;
        internal E_TransShape transShape;
        internal ConsoleColor color;
        internal Pos[]? poses;
        internal int width
        {
            get
            {
                switch (shape)
                {
                    case E_Shape.N04:
                        switch (transShape)
                        {
                            case E_TransShape.Up:
                            case E_TransShape.Down:
                                return 1;
                            case E_TransShape.Left:
                            case E_TransShape.Right:
                                return 4;
                        }
                        break;
                    case E_Shape.N13:
                        switch (transShape)
                        {
                            case E_TransShape.Up:
                            case E_TransShape.Down:
                                return 3;
                            case E_TransShape.Right:
                            case E_TransShape.Left:
                                return 2;
                        }
                        break;
                    case E_Shape.N22:
                        return 2;
                    case E_Shape.N121:
                        switch (transShape)
                        {
                            case E_TransShape.Up:
                            case E_TransShape.Down:
                            case E_TransShape.Right:
                                return 3;
                            case E_TransShape.Left:
                                return 2;
                        }
                        break;
                }
                
                return 1; //一般没有
            }
        }
        public Box()
        {
            
        }
        public Box(E_Shape shape, E_TransShape transShape = E_TransShape.Up)
        {
            this.shape = shape;
            this.transShape = transShape;
            switch (shape)
            {
                case E_Shape.N04:
                    this.color = ConsoleColor.Magenta;
                    break;
                case E_Shape.N13:
                    this.color = ConsoleColor.Green;
                    break;
                case E_Shape.N121:
                    this.color = ConsoleColor.Blue;
                    break;
                case E_Shape.N22:
                    this.color = ConsoleColor.Cyan;
                    break;

            }
        }
        public void move(Pos pos)
        {
            Console.ForegroundColor = color;
            poses = new Pos[4];
            switch (shape)
            {
                //■ ■ ■ ■
                case E_Shape.N04:
                    switch (transShape)
                    {
                        case E_TransShape.Up:
                        case E_TransShape.Down:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x, pos.y + 1);
                            poses[2] = new Pos(pos.x, pos.y + 2);
                            poses[3] = new Pos(pos.x, pos.y + 3);
                            break;
                        case E_TransShape.Left:
                        case E_TransShape.Right:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x + 2, pos.y);
                            poses[2] = new Pos(pos.x + 4, pos.y);
                            poses[3] = new Pos(pos.x + 6, pos.y);
                            break;
                    }
                    break;
                //  ■
                //■ ■ ■ 
                case E_Shape.N121:
                    switch (transShape)
                    {
                        case E_TransShape.Up:
                            poses[0] = new Pos(pos.x + 2, pos.y);
                            poses[1] = new Pos(pos.x, pos.y + 1);
                            poses[2] = new Pos(pos.x + 2, pos.y + 1);
                            poses[3] = new Pos(pos.x + 4, pos.y + 1);
                            break;
                        case E_TransShape.Down:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x + 2, pos.y);
                            poses[2] = new Pos(pos.x + 4, pos.y);
                            poses[3] = new Pos(pos.x + 2, pos.y + 1);
                            break;
                        case E_TransShape.Left:
                            poses[0] = new Pos(pos.x + 2, pos.y);
                            poses[1] = new Pos(pos.x, pos.y + 1);
                            poses[2] = new Pos(pos.x + 2, pos.y + 1);
                            poses[3] = new Pos(pos.x + 2, pos.y + 2);
                            break;
                        case E_TransShape.Right:
                            poses[0] = new Pos(pos.x + 2, pos.y);
                            poses[1] = new Pos(pos.x + 2, pos.y + 1);
                            poses[2] = new Pos(pos.x + 2, pos.y + 2);
                            poses[3] = new Pos(pos.x + 4, pos.y + 1);
                            break;
                    }
                    break;
                //■ ■
                //■ ■
                case E_Shape.N22:
                    switch (transShape)
                    {
                        case E_TransShape.Up:
                        case E_TransShape.Down:
                        case E_TransShape.Left:
                        case E_TransShape.Right:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x, pos.y + 1);
                            poses[2] = new Pos(pos.x + 2, pos.y);
                            poses[3] = new Pos(pos.x + 2, pos.y + 1);
                            break;
                    }
                    break;
                //■ ■ ■
                //■
                case E_Shape.N13:
                    switch (transShape)
                    {
                        case E_TransShape.Up:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x + 2, pos.y);
                            poses[2] = new Pos(pos.x + 4, pos.y);
                            poses[3] = new Pos(pos.x, pos.y + 1);
                            break;
                        case E_TransShape.Down:
                            poses[0] = new Pos(pos.x + 4, pos.y);
                            poses[1] = new Pos(pos.x, pos.y + 1);
                            poses[2] = new Pos(pos.x + 2, pos.y + 1);
                            poses[3] = new Pos(pos.x + 4, pos.y + 1);
                            break;
                        case E_TransShape.Left:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x, pos.y + 1);
                            poses[2] = new Pos(pos.x, pos.y + 2);
                            poses[3] = new Pos(pos.x + 2, pos.y + 2);
                            break;
                        case E_TransShape.Right:
                            poses[0] = pos;
                            poses[1] = new Pos(pos.x + 2, pos.y);
                            poses[2] = new Pos(pos.x + 2, pos.y + 1);
                            poses[3] = new Pos(pos.x + 2, pos.y + 2);
                            break;
                    }
                    break;

            }
        }
        public void write()
        {
            if (poses[0].Equals(default(Pos))) //如果未赋值（没有按AD）
                return;
            //Console.SetCursorPosition(pos.x, pos.y);
            //Console.Write("      ");
            //Console.SetCursorPosition(pos.x, pos.y + 1);
            //Console.Write("      ");

            //Console.SetCursorPosition(pos.x, pos.y + 2);
            //Console.Write("      ");

            foreach (Pos posEach in poses)
            {
                Console.ForegroundColor = color;
                Console.SetCursorPosition(posEach.x, posEach.y);
                Console.Write(grid);
            }
        }
        public void erase()
        {
            if (poses == null) return; 
            foreach (Pos posEach in poses)
            {
                Console.SetCursorPosition(posEach.x, posEach.y);
                Console.Write("  ");
            }
        }
    }

    public struct Pos
    {
        internal int x, y;
        public Pos(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    enum E_Speed
    {
        VeryQuick = 150,
        Quick = 200,
        Normal = 250,
        Slow = 300,

    }
    public enum E_Shape
    {
        N04,N13,N22,N121
    }
    public enum E_TransShape
    {
        Up, Left, Down, Right
    }
   
}